using System;
using System.Configuration;
using System.Web;
using System.Xml;
using System.Xml.Xsl;
using System.Net.Mail;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington	
	Started:	2006-03-08	
	Status:		release	
	Version:	2.0.0
	Buidl:		20080219
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	---------------------------------------------------------------------------	
	Development Notes:
	==================
	20060926:	Move initialiseTemplate() call from _Initialise to _LoadContent
				in order that it is called after _ProcessRequest, because this
				might change one or more of the Parameters.
	20070205:	Added the ability to set the output type. Typically this is used
				together with SetTemplate() to produce display formats other 
				than the default XHtml.
	20070524:	Modification for .Net 2.0
	20070616:	New configuration object
	20070703:	Removed net_2_0 directive (from compatibility build)
	20070829:	Added _ProcessContent override (default brokers)
				Also, Some rearrangement of process, primarily as follows:
				- Form fileds added to user profile immediately after creation
				- loadlinks moved to just after loading of root content. This 
				  occurs in more than one place
	20071123:	Updated decodeContent() for new html nodes (ie @type='html')
	20080204:	Enable recursion in Display(displayException e) override
	20080211:	Device checking.
	20080215:	Add TemplateArguments protected property
	20080219:	Add protected (overridable) _InitialiseTemplate (simply
				calls private initialiseTemplate) to allow custom args to be added
	20080401:	Added template preloading in global.asax (see _GetTemplate())
	---------------------------------------------------------------------------	*/

namespace umlungu.engineX.blogX {
	/// <summary>Display output format enumeration</summary>
	public enum x_displayType {
		/// <summary>Default, ie XHtml</summary>
	    Default = 0,
		/// <summary>Xml</summary>
	    XmlPlain = 1,
		/// <summary>Wml</summary>
	    XmlTransform = 2,
		/// <summary>Something else</summary>
		Other = 3
	}
	/// <summary>
	/// The displayX class.
	/// <para>Purpose: This class is used to display a html page within a blogspace.</para>
	/// <para> - It is intended as a class to be used for general pages where it exposes a single method that is called in the aspx page.</para>
	/// <para>Principles: The following principles apply to the function and use of this class:</para>
	/// <para> - Content for display is contained within an xml structure. This xml is rendered into html using an xsl template.</para>
	/// <para> - The Request object parameters are used to indicate what elements should be included content xml.</para>
	/// <para></para>
	/// <para>History: It has been refactored from the uiX class, which was in turn refactored from the pageX class in version 1.0.2.</para>
	/// <para> - the uiX class is retained for backward compatibility in the assembly, but shares no dependancies with this class</para>
	/// <para></para>
	/// <para></para>
	/// </summary>
	public class displayX : pageX {
		#region Invisible properties
		private string commentAuthor, commentTitle, commentText;
		private string contactName, contactSurname, contactEmail, contactTelno, contactCompany, contactPosition, contactText;
		private string spam_list;

		private char[] delimiter;
		private x_blogspace blogspace;
		#endregion

		#region String constants
		private const string delim = " ,.:";
		private const string root = "<content/>";
		private const string error_topic_name = "Topic is not defined ('topic' parameter missing?)";
		private const string error_content_spam = "Content contains non-permitted words";
		private const string comment_default_author = "[unknown author]";
		private const string comment_default_title = "[no title]";
		private const string comment_default_text = "[no comment]";
		private const string DEFAULT_SPAM_LIST = "http|[url|mailto|fuck|cialis";
		#endregion

		#region Visible properties
		private x_parameters parameters;
		/// <summary>Parameters object</summary>
		/// <value>Object that contains the parameters for the page</value>
		public x_parameters Parameters {
			get { return parameters; }
		}
		/// <summary>The content document element</summary>
		/// <value>An XmlElement</value>
		public XmlElement Content {
			get { return content.DocumentElement; }
		}
		private XmlDocument content;
		/// <summary>The content document</summary>
		/// <value>An XmlDocument</value>
		public XmlDocument Document {
			get { return content; }
		}
		private x_siteprofile siteProfile;
		/// <summary>The site profile object (if enabled), else null</summary>
		/// <value>An x_siteprofile object</value>
		public x_siteprofile SiteProfile {
			get { return siteProfile; }
		}
		private x_userprofile userProfile;
		/// <summary>The user profile object (if enabled), else null</summary>
		/// <value>An x_userprofile object</value>
		public x_userprofile UserProfile {
			get { return userProfile; }
		}
		private XslCompiledTransform template;
		/// <summary>Debugging flag</summary>
		/// <value>Debug set to true if debugging mode anabled</value>
		public XslCompiledTransform Template {
			get { return template; }
			set { template = value; }
		}
		private x_displayType displayType;
		/// <summary>The type of display output format</summary>
		/// <value>Typically </value>
		public x_displayType DisplayType {
			get { return displayType; }
			set { displayType = value; }
		}
		/// <summary>Add a timer mark for performance profiling</summary>
		/// <value>A string identifying the timer mark point</value>
		public string Time {
			set { addTime(value); }
		}

		private XsltArgumentList templateArgs;
		/// <summary>The xsl template arguments</summary>
		/// <value>an XsltArgumentList</value>
		protected XsltArgumentList TemplateArguments {
			get { return templateArgs; }
			//set { templateArgs = value; }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor</summary>
		public displayX() : this("displayX") {
		}
		/// <summary>Constructor supplying class label identifier</summary>
		/// <param name="lbl">Name to be used in error and debugging messages</param>
		public displayX(string lbl) : base(lbl) {
			delimiter = delim.ToCharArray();
			//template = new XslCompiledTransform();	// line deprecated 20080401: created when loaded
			content = new XmlDocument();
			content.LoadXml(root);
			spam_list = Config.SpamList;
		}
		#endregion

		#region Public methods
		/// <overloads>Renders the site page by loading the xml</overloads>
		/// <summary>The 'normal' display method - ie when no exceptions have been raised</summary>
		public virtual void Display() {
			Debugger.Spoor("Display", "link", Request.QueryString["link"]);
			try {
				_Initialise();
				_ProcessRequest();
				_LoadContent();
				_ProcessContent();
				_PostProcessContent();
				// lastly, make sure all the text in blogs elements is decoded
				decodeContent();
				addTime();
				if (Track)
					addProfile();
				if (Debug) {
					displayType = x_displayType.XmlPlain;
				} else if (parameters.Broker == "sitemap") {
					displayType = x_displayType.XmlTransform;
				}
				_WriteContent();
			} catch (displayException e) {
				//Display(e as x_exception);
				Display(e);	// see note 20080204:
			} catch (x_exception e) {
				Display(e);
			} catch (XsltException e) {
				Display(e);
			} catch (Exception e) {
				Display(e);
			}
			Debugger.Save();
		}
		/// <summary>Renders an exception raised as a 'displayException' (effectively a re-direct)</summary>
		/// <param name="e">The exceprion</param>
		public void Display(displayException e) {
			try {
				Debugger.Spoor("Display", e);
				_Initialise(e.Code);
				_ProcessRequest();	// has potential for endless recursion here 
				_LoadContent();
				_ProcessContent();
				_PostProcessContent();
				decodeContent();
				addError(e);
				addTime();
				if (Debug) {
					addProfile();
					displayType = x_displayType.XmlPlain;
				}
				_WriteContent();
			} catch (x_exception err) {
				Display(err);
			} catch (Exception err) {
				Display(err);
			}
		}
		/// <summary>Renders an exception raised as a 'x_exception' (used to display defined error pages)</summary>
		/// <param name="e">The exception</param>
		public void Display(x_exception e) {
			try {
				Debugger.Spoor("Display", e);
				_Initialise(e.Code);
				_LoadContent();
				//_ProcessContent();	- a broker cannot be called within x_exception (only with displayException)
				_PostProcessContent();
				decodeContent();
				addError(e);
				addTime();
				if (Debug) {
					addProfile();
					displayType = x_displayType.XmlPlain;
				}
				_WriteContent();
			}
			catch (Exception err) {
				Display(err);
			}
		}
		/// <summary>Renders an exception raised as a 'XsltException'</summary>
		/// <param name="e">The exception</param>
		public void Display(XsltException e) {
			x_result error = new x_result("ErrorX");
			Response.ContentType = "text/xml";
			Response.Write(error.AddError(e).OuterXml);
		}
		/// <summary>Renders an exception raised as a 'System.Exception' (default)</summary>
		/// <param name="e">The exception</param>
		public void Display(Exception e) {
			x_result error = new x_result("ErrorX");
			Response.ContentType = "text/xml";
			Response.Write(error.AddError(e).OuterXml);
		}

		/// <summary>Gets a querystring parameter (default "")</summary>
		/// <param name="name">Name of the querystring parameter</param>
		public string GetQueryParam(string name) {
			return getRequest(false, name, "");
		}
		/// <summary>Gets a form parameter (default "")</summary>
		/// <param name="name">Name of the f4orm parameter</param>
		public string GetFormParam(string name) {
			return getRequest(true, name, "");
		}
		/// <overloads>Sets the active template</overloads>
		/// <summary>Template filename supplied</summary>
		public void SetTemplate(string filename) {
			getTemplate(filename);
		}
		/// <summary>Template object supplied</summary>
		public void SetTemplate(XslCompiledTransform tmpl) {
			template = tmpl;
		}
		#endregion
		
		#region Protected methods
		/// <overloads>Initialises the object</overloads>
		/// <summary></summary>
		protected virtual void _Initialise() {
			if (Config.SiteProfile == "yes") {
				siteProfile = (x_siteprofile)Application["SiteProfileX"];
				//Debugger.Spoor("_Initialise", siteProfile.ProfileXml);
			}
			if (Config.UserProfile == "yes") {
				userProfile = (x_userprofile)Session["UserProfileX"];
				UserProfile.Add(Request);
				//Debugger.Spoor("_Initialise", userProfile.ProfileXml);
			}
			parameters = new x_parameters(Config.Space, Application, this);
			Debugger.Spoor("_Initialise", "link", parameters.LinkName);
			Debugger.Spoor("_Initialise", "parameters", parameters.Name);
			Debugger.Spoor("_Initialise", "secure", (parameters.Secure) ? "yes" : "no");
			checkSecure();
			blogspace = new x_blogspace(Application);
			Debugger.Spoor("_Initialise", "blogspace");
			//initialiseTemplate(); in _LoadContent()- from 1.0.3
			loadLinks();
		}
		/// <summary>Initialises the object for an erorr code</summary>
		protected virtual void _Initialise(string errorcode) {
			Debugger.Spoor("_Initialise", "errorcode", errorcode);
			content.LoadXml(root);
			parameters.ReLoad(errorcode);
			loadLinks();
		}
		/// <summary>
		/// Does the processing of the page request. 
		///	This method is typically overriden in derived classes in order to respond to the request before displaying the page.
		///	Example: this method can be overridden by a method that reads a form and submits values to the back-end.
		/// </summary>
		protected virtual void _ProcessRequest() {
			_ProcessRequest(true);
		}
		protected virtual void _ProcessRequest(bool RunDefaultBrokers) {
			Debugger.Spoor("_ProcessRequest");
			if (RunDefaultBrokers) {// Handle some standard situations, eg comment, contact
				switch (parameters.Broker) {
					case "comment":	if (loadComment())
										postComment();
									break;
					case "contact":	if (loadContact())
										sendContact();
									break;
				}
			}
		}
		/// <summary>
		/// Loads the content xml
		/// </summary>
		protected virtual void _LoadContent() {
			Debugger.Spoor("_LoadContent", Parameters.Device);
			if (Debugger.Enabled) {
				Content.SetAttribute("name", parameters.Name);
				Content.SetAttribute("space", parameters.Space);
				Content.SetAttribute("topic", parameters.Topic);
				Content.SetAttribute("broker", parameters.Broker);
				Content.SetAttribute("provider", parameters.Provider);
				Content.SetAttribute("spaces", parameters.Spaces);
				Content.SetAttribute("topics", parameters.Topics);
				Content.SetAttribute("blogs", parameters.Blogs);
				Content.SetAttribute("link", parameters.LinkName);
			}
			_InitialiseTemplate();
			loadSpaces();
			loadTopics();
			loadBlogs();
			loadExtra();
			loadPage();
			//loadForm();	// appears to be made redundant because of loadFields
			loadFrame();
		}
		/// <summary>
		/// Does the post-processing of the page content.
		///	This method is often overridden in derived classes in order to append or manipulate the content xml before displaying the page.
		///	Example: this method can be overridden by a method that calls a web service and appends xml from the web service result.
		/// NB: If the form is overridden, the base() method must be called for page and form processing.
		/// </summary>
		protected virtual void _ProcessContent() {
			Debugger.Spoor("_ProcessContent");
		}
		/// <summary>
		/// Does the writing of the page content to output stream in the appropriate format.
		/// </summary>
		protected virtual void _WriteContent() {
			Debugger.Spoor("_WriteContent", Parameters.Device);
			Debugger.Spoor("_WriteContent", Content);
			switch (displayType) {
				case x_displayType.XmlPlain:		Response.ContentType = "text/xml";
													Response.Write(content.OuterXml);
													break;
				case x_displayType.XmlTransform:	Response.ContentType = "text/xml";
													template.Transform(content, templateArgs, Response.Output);
													break;
				case x_displayType.Default:
				default:							Response.ContentType = "text/html";
													Debugger.Spoor("_WriteContent", "Transform.before");	
													template.Transform(content, templateArgs, Response.Output);
													Debugger.Spoor("_WriteContent", "Transform.after");
													addTime("done:");
													break;
			}
			Debugger.Spoor("_WriteContent", "End.");
		}
		/// <summary>
		/// Gets the xslt template to be used for rendering to html.
		///	- The default template is the blog defined template.
		///	- When a page is defined as a "sitemap" (ie Google sitemap) the template is the templates/sitemapX.xsl template
		/// This can be overridden to load a custom template
		/// </summary>
		protected virtual void _GetTemplate() {
			Debugger.Spoor("_GetTemplate", parameters.Name);
			if (parameters.Broker == "sitemap") {
				SetTemplate("sitemapX.xsl");
			} else {
				if (Config.TemplatePreload == "yes")
					SetTemplate(Application[Config.Template] as XslCompiledTransform);
				else
					SetTemplate(blogspace.GetTransform(parameters.Name));

			}
			Debugger.Spoor("_GetTemplate", "got");
		}
		/// <summary>
		/// Does the post-processing of the page content, including expanding form fields from site and user profile.
		///	This method can be overridden in derived classes in order to finally manipulate the content xml before displaying the page.
		///	Example: this method can be overridden by a method that calls a web service and appends xml from the web service result.
		/// NB: If the form is overridden, the base() method must be called for form field post-processing.
		/// </summary>
		protected virtual void _PostProcessContent() {
			Debugger.Spoor("_PostProcessContent");
			loadFields();
			expandIncludes();
		}
		/// <summary>
		/// Adds the profile xml to the content xml. 
		/// This method is typically called by derived classes in the overridden ProcessContent method.
		///	Example: This method can be used to add site profile info to the content xml in order to make the vales in the site profile
		/// avaiable to every page on the site. Similarly with user profile xml.
		/// </summary>
		/// <param name="SiteProfileFlag">A flag to indicate if the site profile is to be added</param>
		/// <param name="UserProfileFlag">A flag to indicate if the user profile is to be added</param>
		protected virtual void _AddProfile(bool SiteProfileFlag, bool UserProfileFlag) {
			addProfile(SiteProfileFlag, UserProfileFlag);
		}

		/// <summary>
		/// Loads the xsl template and passes the Request parameters to it
		/// </summary>
		protected virtual void _InitialiseTemplate() {
			initialiseTemplate();
		}

		/// <summary>
		/// Method to write some debugging information as a comment in the output html
		/// </summary>
		protected void _Debug(string label, params string[] outparam) {
			if (Debug) {
				Response.Write(String.Concat("\n<!-- ", label, "."));
				Response.Write(label);
				Response.Write("::\n");
				for (int i=0; i < outparam.Length; i++)
					Response.Write(outparam[i]);
				Response.Write("\n");
				Response.Write("-->\n");
			}
		}
		#endregion
				
		#region Private methods
		/// <summary>
		/// Loads the xsl template and passes the Request parameters to it
		/// </summary>
		private void initialiseTemplate() {
			// load the xslt template (note: this may be overridden in derived classes)
			_GetTemplate();
			//	Add xsl parameters.
			templateArgs = new XsltArgumentList();
			string protocol = (parameters.Secure)? "https://" : "http://";
			templateArgs.AddParam("tick", "", DateTime.Now.Ticks.ToString());
			templateArgs.AddParam("host", "", Config.Host);
			templateArgs.AddParam("site", "", String.Concat(protocol, Request.Url.Host, Config.Path));
			templateArgs.AddParam("name", "", parameters.Name);
			templateArgs.AddParam("spaces", "", parameters.Spaces);
			templateArgs.AddParam("topic", "", parameters.Topic);
			templateArgs.AddParam("topics", "", parameters.Topics);
			templateArgs.AddParam("blogs", "", parameters.Blogs);
			templateArgs.AddParam("broker", "", parameters.Broker);
			templateArgs.AddParam("link", "", parameters.LinkName);
			templateArgs.AddParam("device", "", parameters.Device);
			templateArgs.AddParam("secure", "", (parameters.Secure)? "yes" : "no");
		}
		/// <summary>
		/// Loads the links xml (if required)
		/// </summary>
		private void loadLinks() {
			Debugger.Spoor("loadLinks");
			if (parameters.Type == x_paramtype.link) {
				content.DocumentElement.AppendChild(content.ImportNode(parameters.Links.DocumentElement, true));
			}
		}
		/// <summary>
		/// Loads the blogspace xml
		/// </summary>
		private void loadSpaces() {
			if (parameters.Spaces == "all") {
				// special case...
			} else {
				string[] spaceList = parameters.Spaces.Split(delimiter);
				foreach(string spaceName in spaceList) {
					blogspace.Get(spaceName);
					content.DocumentElement.AppendChild(content.ImportNode(blogspace.Blogspace, true));
				}
			}
		}
		/// <summary>
		/// Loads the blogtopic xml
		/// </summary>
		private void loadTopics() {
			if (parameters.Topics == "all" || parameters.Topics == "blogs") {
				foreach (XmlNode topicNode in content.SelectNodes("//topics/topic")) {
					((XmlElement)topicNode).SetAttribute("blogs", "yes");
				}
			} else {
				foreach (XmlNode topicNode in content.SelectNodes("//topics/topic")) {
					if (parameters.Topics.IndexOf(((XmlElement)topicNode).GetAttribute("name")) < 0) {
						XmlNode topicblog = topicNode.SelectSingleNode("blog");
						if (topicblog != null)
							topicNode.RemoveChild(topicblog);
						((XmlElement)topicNode).SetAttribute("blogs", "no");
					} else {
						((XmlElement)topicNode).SetAttribute("blogs", "yes");
					}
				}
			}
		}
		/// <summary>
		/// Loads the blog xml
		/// </summary>
		private void loadBlogs() {
			x_blog thisblog = new x_blog(Application);
			foreach (XmlNode topicNode in content.SelectNodes("//topics/topic[@blogs='yes']")) {
				string thisspace = ((XmlElement)(topicNode.ParentNode).ParentNode).GetAttribute("name");
				thisblog.Get(thisspace, ((XmlElement)topicNode).GetAttribute("name"));
				topicNode.AppendChild(content.ImportNode(thisblog.Blogs.SelectSingleNode("/blogX/blogs"), true));
			}
			// now remove blog detail, unless specified
			if (parameters.Blogs != "all") {
				foreach (XmlNode blogNode in content.SelectNodes("//topic/blogs/blog")) {
					if (parameters.Blogs.IndexOf(((XmlElement)blogNode).GetAttribute("id")) < 0) {
						foreach (XmlNode blogtext in blogNode.SelectNodes("text | bloghtml | blogtext")) {
							blogNode.RemoveChild(blogtext);
						}
					}
				}
			}
		}
		/// <summary>
		/// Loads and appends any extra data
		/// </summary>
		/// <remarks>
		/// All errors in loading the extra xml data (file existence or parseError) are caught.
		/// This means errors result in the page loading without the extra xml data.
		/// </remarks>
		private void loadExtra() {
			XmlDocument extraPage = new XmlDocument();
			try {
				extraPage.Load(String.Concat(Application["ExtraPathX"], parameters.Name, "/page.xml"));
				XmlNode data = extraPage.DocumentElement.SelectSingleNode(String.Concat("//", parameters.Name));
				if (data != null)
					content.DocumentElement.AppendChild(content.ImportNode(extraPage.DocumentElement, true));
			} catch(System.Exception e) {
				if (Debug) {
					x_result error = new x_result("BlogX");
					x_exception xe = new x_exception("error_extra", e.Message);
					error.AddError(xe);
					content.DocumentElement.AppendChild(content.ImportNode(error.Result.DocumentElement, true));
				}
			}
		}
		/// <summary>
		/// Loads and appends page xml (if required)
		/// </summary>
		/// <remarks>
		///	- Adds the page xml (if the parmaters type is a link with a page reference)
		///	- Checks for a form, creates a x_form object to prepopulate the form
		/// </remarks>
		private void loadPage() {
			try {
				if (Parameters.Type == x_paramtype.link) {
					XmlElement pageEl = Parameters.LinkPage;
					if (pageEl != null)
						loadPage(pageEl);
				}
			}
			catch (XmlException e) {
				throw (new x_exception("error_page_load", String.Concat(e.Message, " at ", e.LineNumber.ToString(), ":", e.LinePosition.ToString())));
			}
			catch (Exception e) {
				throw (new x_exception("error_page_load", String.Concat(e.Message, " : ", e.StackTrace)));
			}
		}
		/// <summary>Load a page defined in the xml element</summary>
		private void loadPage(XmlElement pageElem) {
			x_page thispage = new x_page(this);
			thispage.Load(parameters.Section, parameters.Subsect, parameters.Process, parameters.File);
			Content.AppendChild(Document.ImportNode(thispage.Page, true));
		}
		/// <summary>
		/// Loads and appends field data
		/// </summary>
		private void loadFields() {
			Debugger.Spoor("loadFields");
			x_form thisform = new x_form(this);
			thisform.Load(content);
		}
		/// <summary>
		/// Loads and appends form data (if required)
		/// </summary>
		private void loadForm() {
			XmlElement formEl = (XmlElement)Content.SelectSingleNode("//fields");
			if (formEl != null)
				loadForm(formEl);
		}
		/// <summary>
		/// Loads and appends form data
		/// </summary>
		private void loadForm(XmlElement formElem) {
			Debugger.Spoor("loadForm");
			x_form thisform = new x_form(this);
			thisform.Load(formElem);
		}
		/// <summary>
		/// Loads and appends the frame element (if required)
		/// </summary>
		private void loadFrame() {
			if (Parameters.Type == x_paramtype.link) {
				XmlElement frameEl = Parameters.LinkFrame;
				if (frameEl != null)
					Content.AppendChild(Document.ImportNode(frameEl, true));
			}
		}

		/// <summary>
		/// Expands the include elements with included references
		/// format of the include element:
		///		<include type="site/user" field=""/select="" />
		/// </summary>
		private void expandIncludes() {
			foreach (XmlNode include in Content.SelectNodes("//include")) {
				expandInclude(include as XmlElement);
			}
		}
		private void expandInclude(XmlElement include) {
			string incType = include.GetAttribute("type");
			switch (incType) {
				case "site": siteProfile.Replace(include);	break;
				case "user": userProfile.Replace(include); break;
			}
		}

		/// <summary>
		/// Loads the blog xml
		/// </summary>
		private void decodeContent() {
			// the following is for backward compatability
			foreach (XmlNode textnode in content.SelectNodes("//blog/text")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = (textel.GetAttribute("type") == "html" || textel.GetAttribute("html") == "yes")? HttpUtility.HtmlDecode(textel.InnerText) : HttpUtility.HtmlDecode(textel.InnerXml);
			}
			foreach (XmlNode textnode in content.SelectNodes("//blog/bloghtml")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = HttpUtility.HtmlDecode(textel.InnerText);
			}
			foreach (XmlNode textnode in content.SelectNodes("//blog/blogtext")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = HttpUtility.HtmlDecode(textel.InnerXml);
			}
			foreach (XmlNode textnode in content.SelectNodes("//blog/*[@type='html']")) {
				XmlElement textel = (XmlElement)textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = HttpUtility.HtmlDecode(textel.InnerText);
			}
		}
		/// <summary>
		/// Loads the comment form field values and checks for spam
		/// </summary>
		private bool loadComment() {
			commentAuthor = (Request.Form["Comment_name"] == null)? comment_default_author: Request.Form["Comment_name"];
			commentTitle = (Request.Form["Comment_title"] == null)? comment_default_title: Request.Form["Comment_title"];
			commentText = (Request.Form["Comment"] == null)? comment_default_text: Request.Form["Comment"];
			bool titlespam = spamCheck(spam_list, commentTitle, false);
			bool textspam = spamCheck(spam_list, commentText, false);
			bool spam = (titlespam || textspam);
			return !spam;
		}
		/// <summary>
		/// Adds a comment to a blog
		/// </summary>
		private void postComment() {
			if (parameters.Spaces == null)
				throw(new x_exception("error_topic_name", error_topic_name));
			if (commentTitle != comment_default_title && commentText != comment_default_text) {
				x_blog blogx = new x_blog(Application);
				blogx.AddComment(parameters.Space, parameters.Topic, System.Convert.ToInt32(parameters.Blog), "public", commentAuthor, commentTitle, commentText);
				//Response.Write(String.Concat("\n<!-- Comment: '", space, ".", topic, "\nauthor:", commentAuthor, "\ntitle:", commentTitle, "\ncomment:", commentText, "' -->\n"));
			}
		}
		/// <summary>
		/// Loads the contact form field values and checks for spam
		/// </summary>
		private bool loadContact() {
			contactName = getRequest(true, "Contact_name", "-");
			contactSurname = getRequest(true, "Contact_surname", "-");
			contactEmail = getRequest(true, "Contact_email", "-");
			contactTelno = getRequest(true, "Contact_telno", "-");
			contactCompany = getRequest(true, "Contact_company", "-");
			contactPosition = getRequest(true, "Contact_position", "-");
			contactText = getRequest(true, "Contact_message", "-");
			bool spam = spamCheck(spam_list, contactText, false);
			//Response.Write(String.Concat("<!-- contact spam: ", spam.ToString(), "-->"));
			return !spam;
		}
		/// <summary>
		/// Emails a content request to the configured email address
		/// </summary>
		private void sendContact() {
			try {
				string email = (Config.EmailTo == null)? "customer@umlungu.com" : Config.EmailTo;
				string from = (Config.EmailFrom == null)? "EngineX" : Config.EmailFrom;
				string subject = (Config.EmailSubject == null)? "EngineX contact message" : Config.EmailSubject;
				string message = String.Concat("Name: ", contactName, "\nSurname: ", contactSurname, "\nEmail: ", contactEmail, "\nTelephone: ", contactTelno, "\nCompany: ", contactCompany, "\nPosition: ", contactPosition, "\nText: ", contactText);
				System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();
				//smtp.Host = "localhost";	// for whatever reason doesnt work on umlungu server
				smtp.Send(from, email, subject, message);
				//Response.Write(String.Concat("\n<!-- Sent email: '", "\nemail:", email, "\nfrom:", from, "\nsubject:", subject, "\nmessage:", message, "' -->\n"));
			} catch(x_exception e) {
				throw e;
			} catch(System.Exception e) {
				Response.Write(String.Concat("\n<!-- Could not send email: '", e.Message, "' -->\n"));
			}
		}
		#endregion
				
		#region utility methods
		/// <summary>
		/// Looks for web spam content and removes
		///	Expand as necassary
		/// </summary>
		private bool isSpam(string input, bool raiseError) {
			if (input.IndexOf("http:") >= 0) {
				if (raiseError)
					throw(new x_exception("error_content_spam", error_content_spam));
				else
					return true;
			}
			return false;
		}
		/// <overloads>Place iming mark for performance profiles</overloads>
		/// <summary>
		/// Mark placed in xml as attribute of content node
		/// </summary>
		private void addTime() {
			DateTime endtime = DateTime.Now;
			TimeSpan timetaken = endtime - StartTime;
			string thistime = timetaken.TotalSeconds.ToString();
			content.DocumentElement.SetAttribute("time", thistime);
			content.DocumentElement.SetAttribute("ticks", endtime.Ticks.ToString());
		}
		/// <summary>
		/// Mark placed in html as html comment
		/// </summary>
		private void addTime(string label) {
			DateTime endtime = DateTime.Now;
			TimeSpan timetaken = endtime - StartTime;
			string thistime = timetaken.TotalSeconds.ToString();
			Response.Write(String.Concat("<!--", label, thistime, "-->"));
		}
	/* 	Comment this in favour of the 1.0.3 version below, since this uses the declared property
		/// <summary>
		/// Loads the xsl template for special types (ie not the normal content xsl)
		/// </summary>
		private XslTransform getTemplate(string thistype) {
			XslTransform templ = new XslTransform();
			//string templFile = String.Concat(blogspace.Path, parameters.Name, "/", thistype);
			string templFile = Server.MapPath(String.Concat("templates/", thistype));
			templ.Load(templFile);
			//throw(new x_exception("Template file", templFile));
			return(templ);
		}
	*/
		/// <summary>
		/// Loads the xsl template for special types (ie not the normal content xsl)
		/// </summary>
		private void getTemplate(string thistype) {
			template = new XslCompiledTransform();
			string templFile = String.Concat(blogspace.Path, parameters.Name, "/", thistype);
			template.Load(templFile);
		}
		/// <summary>Adds the profile xml (for debugging)</summary>
		private void addProfile() {
			addProfile(true, true);
		}
		/// <summary>Adds the profile xml (for debugging)</summary>
		private void addProfile(bool siteFlag, bool userFlag) {
			if (siteProfile != null && siteFlag)
				Content.AppendChild(content.ImportNode(siteProfile.Profile, true));
			if (userProfile != null && userFlag)
				Content.AppendChild(content.ImportNode(userProfile.Profile, true));
		}
		/// <overloads>Adds a element containg exception information to the content</overloads>
		/// <summary>Error from an x_exception</summary>
		private void addError(x_exception err) {
			XmlElement errel = content.CreateElement("error");
			errel.SetAttribute("name", err.Code);
			XmlElement msgel = (XmlElement)errel.AppendChild(content.CreateElement("message"));
			msgel.InnerText = err.Message;
			XmlElement trcel = (XmlElement)errel.AppendChild(content.CreateElement("stacktrace"));
			trcel.InnerText = err.StackTrace;
			Content.AppendChild(errel);
		}
		/// <overloads>
		/// Looks for web spam content and removes
		///	- from x_broker
		/// </overloads>
		/// <summary>
		///	specified spam word list
		/// </summary>
		private bool spamCheck(string checkList, string input, bool raiseError) {
			bool retval = false;
			try {
				string test = input.ToLower();
				string delimStr = "|";
			    char [] delimiter = delimStr.ToCharArray();
				string [] split = null;
				split = checkList.Split(delimiter);
				foreach (string s in split) {
					if (test.IndexOf(s) >= 0) {
						retval = true;
						break;
					}
	            }
			} catch (Exception e) {
				throw(new x_exception("error_content_spam", String.Concat(e.Message, " : ", e.StackTrace)));
			}
			
			if (raiseError && retval)
				throw(new x_exception("error_content_spam", error_content_spam));
			return retval;
		}
		/// <summary>
		///	default spam word list
		/// </summary>
		private bool spamCheck(string input, bool raiseError) {
			return spamCheck(spam_list, input, raiseError);
		}
		/// <summary>Check for secure link parameter and re-direct if not secure</summary>
		private void checkSecure() {
			Uri thisurl = Request.Url;
			bool isSecure = (Request.ServerVariables["HTTPS"] == "on");
			if (parameters.Secure && !isSecure)
				Response.Redirect(String.Concat("https://", thisurl.Host, thisurl.PathAndQuery));
			else if (!parameters.Secure && isSecure)
				Response.Redirect(String.Concat("http://", thisurl.Host, thisurl.PathAndQuery));
		}
		#endregion
	}
}
